package com.foreverwin.jzymes.production.sfc.service.impl;

import com.foreverwin.jzymes.common.constant.Constants;
import com.foreverwin.jzymes.common.mapper.SfcBatchPassLogAdviceMapper;
import com.foreverwin.jzymes.common.service.*;
import com.foreverwin.jzymes.common.util.BOHandleUtil;
import com.foreverwin.jzymes.common.util.CommonMethods;
import com.foreverwin.jzymes.framework.exception.BusinessException;
import com.foreverwin.jzymes.framework.util.I18nUtil;
import com.foreverwin.jzymes.production.sfc.domain.SFC;
import com.foreverwin.jzymes.production.sfc.enums.ProcedureEnum;
import com.foreverwin.jzymes.production.sfc.mapper.SFCAdviceMapper;
import com.foreverwin.jzymes.production.sfc.service.ISFCService;
import com.foreverwin.jzymes.production.sfc.service.ISFCServiceAdvice;
import com.sap.me.demand.SFCBOHandle;
import com.sap.me.demand.SFCBOInterface;
import com.sap.me.extension.Services;
import com.sap.me.frame.BOHandle;
import com.sap.me.frame.BasicBOBeanException;
import com.sap.me.frame.Data;
import com.sap.me.frame.ServiceLocator;
import com.sap.me.frame.transitionutils.Exceptions;
import com.sap.me.numbering.GenerateNextNumberRequest;
import com.sap.me.numbering.GenerateNextNumberResponse;
import com.sap.me.numbering.NextNumberTypeEnum;
import com.sap.me.numbering.NumberingServiceInterface;
import com.sap.me.production.AdjustSfcQuantityRequest;
import com.sap.me.production.DeleteSfcRequest;
import com.sap.me.production.ScrapDeleteServiceInterface;
import com.sap.me.production.SfcStateServiceInterface;
import com.visiprise.frame.service.ext.SecurityContext;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.math.BigDecimal;
import java.util.*;

/**
 * 工令 服务层增强实现
 *
 * @author ervin
 * @date 2019-03-13
 */
@Service
public class SFCServiceAdviceImpl implements ISFCServiceAdvice {

    @Autowired
    ISFCService sfcService;
    @Autowired
    ISfcStepAdviceService sfcStepAdviceService;
    @Autowired
    ISystemRuleAdviceService systemRuleAdviceService;
    @Autowired
    IFieldGroupAdviceService fieldGroupAdviceService;
    @Autowired
    ISiteAdviceService siteAdviceService;
    @Autowired
    IItemGroupAdviceService itemGroupAdviceService;
    @Autowired
    IItemAdviceService itemAdviceService;
    @Autowired
    ICustomFieldsAdviceService customFieldsAdviceService;
    @Autowired
    SFCAdviceMapper sfcAdviceMapper;
    @Autowired
    SfcBatchPassLogAdviceMapper sfcBatchPassLogAdviceMapper;

    /**
     * 创建工令
     *
     * @param sfc
     */
    @Transactional( propagation = Propagation.REQUIRED, rollbackFor = Exception.class )
    public void createSFC( SFC sfc ){

        /**检查相关字段*/
        checkFieldsForCreate( sfc );
        /**检查当天工令是否已创建*/
        checkSFCCreatedByDate( sfc );
        /**检查计划开始日期是否合法*/
        checkPlanStartDate( sfc );

        /**CNC制程才可创建MES可追溯SFC*/
        String procedure = fieldGroupAdviceService.getFieldName( sfc.getProcedureBo() );
        if( ProcedureEnum.CNC == ProcedureEnum.valueOf( procedure ) ){
            createMesSFC( sfc );
        }else{
            generateSFCCode( sfc );
        }
        // @Test
        /*sfc.setSfc( "11137" );
        sfc.setHandle( BOHandleUtil.buildSFCHandle( sfc.getSite(), sfc.getSfc() ) );*/
        sfc.setStatusBo( "StatusBO:"+ sfc.getSite() +",401" );
        sfc.setCreatedDateTime( new Date() );
        sfc.setCreateUserBo( CommonMethods.getUserBo() );
        sfcService.insertSFC( sfc );

        /**保存工令计划数量*/
        customFieldsAdviceService.updateCustomField( sfc.getItemBo(), Constants.ITEM_CUSTOM_FIELD_PLAN_QTY, sfc.getPlanQty().toString() );
    }

    /**
     * 创建前检查相关字段
     *
     * @param sfc
     */
    public void checkFieldsForCreate( SFC sfc ){
        /**站点不能为空*/
        siteAdviceService.existSite( sfc.getSite() );
        /**制程不能为空*/
        sfc.setProcedureBo( BOHandleUtil.buildFieldGroupHandle( sfc.getSite(), sfc.getProcedureBo() ) );
        fieldGroupAdviceService.existFieldGroup( sfc.getProcedureBo() );
        /**机种不能为空*/
        String itemGroup = itemGroupAdviceService.findItemGroupBySite( sfc.getSite() );
        Assert.hasText( itemGroup, I18nUtil.getI18nText( "common.itemGroupNotExist", new String[]{ itemGroup } ) );
        sfc.setMachineType( itemGroup );
        /**物料不能为空*/
        Map<String,Object> itemMap = itemAdviceService.existItem( sfc.getItemBo() );
        /**检查工艺路线*/
        if( sfc.getProcedureBo().equals( BOHandleUtil.buildFieldGroupHandle( sfc.getSite(), ProcedureEnum.CNC.name() ) ) ){
            Assert.hasText( (String) itemMap.get( "ROUTER_BO" ), I18nUtil.getI18nText( "production.sfc.routerOnItemNotExist", new Object[]{ itemMap.get( "ITEM" ) } ) );
            sfc.setRouterBo( (String) itemMap.get( "ROUTER_BO" ) );
        }
        /**计划生产数量不能为空*/
        Assert.notNull( sfc.getPlanQty(), I18nUtil.getI18nText( "production.sfc.planQtyNotNull" ) );
        /**工令数量不能为空*/
        Assert.notNull( sfc.getSfcQty(), I18nUtil.getI18nText( "production.sfc.sfcQtyNotNull" ) );
        /**计划开始日期不能为空*/
        Assert.notNull( sfc.getPlanStartTime(), I18nUtil.getI18nText( "production.sfc.planStartTimeNotNull" ) );
        /**工令数量必须大于0*/
        Assert.isTrue( sfc.getSfcQty() > 0, I18nUtil.getI18nText( "production.sfc.sfcQtyGreaterThanZero" ) );
    }

    /**
     * 修改工令
     *
     * @param sfc
     */
    @Transactional( propagation = Propagation.REQUIRED, rollbackFor = Exception.class )
    public void updateSFC( SFC sfc ){

        /**检查修改相关字段*/
        checkFieldsForUpdate( sfc );

        String handle = BOHandleUtil.buildSFCHandle( sfc.getSite(), sfc.getSfc() );
        SFC originalSFC = sfcService.selectSFCById( handle );

        /**工令号对应的工令是否存在*/
        Assert.notNull( originalSFC, I18nUtil.getI18nText( "production.sfc.sfcNotExist", new String[]{ sfc.getSfc() } ) );

        /**检查工令是否已经完结，完结工令不允许修改*/
        Assert.isNull( originalSFC.getActEndTime(), I18nUtil.getI18nText("production.sfc.sfcDone", new String[]{ sfc.getSfc() } ) );

        /**工令制程不允许更改*/
        Assert.isTrue( sfc.getProcedureBo().equals( originalSFC.getProcedureBo() ), I18nUtil.getI18nText( "production.sfc.procedureChange" ) );

        /**工令物料不允许更改*/
        Assert.isTrue( sfc.getItemBo().equals( originalSFC.getItemBo() ), I18nUtil.getI18nText( "production.sfc.itemChange" ) );

        /**研磨修改后的工令数量不允许小于已下达数量；CNC修改后的工令数量不允许小于（已下达数量+第一个工序待过站数量）*/
        checkSfcQtyForUpdate( sfc, originalSFC );

        /**研磨存在已下达数量的工令，计划开始日期不允许更改；CNC存在已下达数量或存在第一个工序待过站数量的工令，计划开始日期不允许更改*/
        checkPlanStartTimeForUpdate( sfc, originalSFC );

        /**CNC调整工令数量*/
        String procedure = fieldGroupAdviceService.getFieldName( sfc.getProcedureBo() );
        if( ProcedureEnum.CNC == ProcedureEnum.valueOf( procedure )  ){
            adjustMesSfcQty( sfc );
        }

        /**保存修改*/
        SFC _sfc = new SFC();
        _sfc.setHandle( originalSFC.getHandle() );
        _sfc.setPlanQty( sfc.getPlanQty() );
        _sfc.setSfcQty( sfc.getSfcQty() );
        _sfc.setPlanStartTime( sfc.getPlanStartTime() );
        _sfc.setUpdatedDateTime( new Date() );
        _sfc.setUpdateUserBo( CommonMethods.getUserBo() );
        sfcService.updateSFC( _sfc );

        /**保存工令计划数量*/
        customFieldsAdviceService.updateCustomField( sfc.getItemBo(), Constants.ITEM_CUSTOM_FIELD_PLAN_QTY, sfc.getPlanQty().toString() );
    }

    /**
     * 检查更改后计划开始时间
     *
     * @param sfc
     * @param originalSFC
     */
    public void checkPlanStartTimeForUpdate( SFC sfc, SFC originalSFC ){

        if( !sfc.getPlanStartTime().equals( originalSFC.getPlanStartTime() ) ){
            /*String procedure = fieldGroupAdviceService.getFieldName( sfc.getProcedureBo() );
            if( ProcedureEnum.CNC == ProcedureEnum.valueOf( procedure )  ){
                String operation = sfcStepAdviceService.findSfcFirstOperation( originalSFC.getHandle() );
                Long pendingPassQty = sfcBatchPassLogAdviceMapper.selectPendingPassQtyByOperation( sfc.getSite(), sfc.getSfc(), operation );
                if( originalSFC.getReleasedQty() != null || pendingPassQty != null ){
                    throw BusinessException.build( "production.sfc.sfcPlanStartTimeChange", null );
                }
            }else{
                if( originalSFC.getReleasedQty() != null ){
                    throw BusinessException.build( "production.sfc.sfcPlanStartTimeChange",null );
                }
            }*/
            /**工令存在已下达数量，不允许修改计划开始日期*/
            if( originalSFC.getReleasedQty() != null && originalSFC.getReleasedQty() > 0 ){
                throw BusinessException.build( "production.sfc.sfcPlanStartTimeChange", null );
            }

            /**检查更改后的计划开始日期已经开立工令*/
            checkSFCCreatedByDate( sfc );

            /**检查计划开始日期是否在允许范围*/
            checkPlanStartDate( sfc );
        }
    }

    /**
     * 检查更改后的工令数量
     *
     * @param sfc
     * @param originalSFC
     */
    public void checkSfcQtyForUpdate( SFC sfc, SFC originalSFC ){

        if( sfc.getSfcQty() < originalSFC.getSfcQty() ){
            /*String procedure = fieldGroupAdviceService.getFieldName( sfc.getProcedureBo() );
            if( ProcedureEnum.CNC == ProcedureEnum.valueOf( procedure )  ){
                String operation = sfcStepAdviceService.findSfcFirstOperation( originalSFC.getHandle() );
                Long pendingPassQty = sfcBatchPassLogAdviceMapper.selectPendingPassQtyByOperation( sfc.getSite(), sfc.getSfc(), operation );
                pendingPassQty = pendingPassQty == null ? 0 : pendingPassQty;
                if( sfc.getSfcQty() < ( originalSFC.getReleasedQty() + pendingPassQty ) ){
                    throw BusinessException.build( "production.sfc.sfcQtyLessThanReleasedQtyAndPendingPassQty", new Long[]{ sfc.getSfcQty(), originalSFC.getReleasedQty(), pendingPassQty } );
                }
            }else{
                if( sfc.getSfcQty() < originalSFC.getReleasedQty() ){
                    throw BusinessException.build( "production.sfc.sfcQtyLessThanReleasedQty", new Long[]{ sfc.getSfcQty(), originalSFC.getReleasedQty() } );
                }
            }*/
            /**修改后的工令数量不允许小于已下达数量*/
            if( sfc.getSfcQty() < originalSFC.getReleasedQty() ){
                throw BusinessException.build( "production.sfc.sfcQtyLessThanReleasedQty", new Long[]{ sfc.getSfcQty(), originalSFC.getReleasedQty() } );
            }
        }
    }

    /**
     * 检查修改相关字段
     *
     * @param sfc
     */
    public void checkFieldsForUpdate( SFC sfc ){

        /**站点不能为空*/
        siteAdviceService.existSite( sfc.getSite() );
        /**制程不能为空*/
        sfc.setProcedureBo( BOHandleUtil.buildFieldGroupHandle( sfc.getSite(), sfc.getProcedureBo() ) );
        fieldGroupAdviceService.existFieldGroup( sfc.getProcedureBo() );
        /**机种不能为空*/
        String itemGroup = itemGroupAdviceService.findItemGroupBySite( sfc.getSite() );
        Assert.hasText( itemGroup, I18nUtil.getI18nText( "common.itemGroupNotExist", new String[]{ itemGroup } ) );
        sfc.setMachineType( itemGroup );
        /**物料不能为空*/
        itemAdviceService.existItem( sfc.getItemBo() );
        /**工令号不能为空*/
        Assert.hasText( sfc.getSfc(), I18nUtil.getI18nText("common.sfcNotBlank") );
        /**计划生产数量不能为空*/
        Assert.notNull( sfc.getPlanQty(), I18nUtil.getI18nText( "production.sfc.planQtyNotNull" ) );
        /**工令数量不能为空*/
        Assert.notNull( sfc.getSfcQty(), I18nUtil.getI18nText( "production.sfc.sfcQtyNotNull" ) );
        /**工令数量必须大于0*/
        Assert.isTrue( sfc.getSfcQty() > 0, I18nUtil.getI18nText( "production.sfc.sfcQtyGreaterThanZero" ) );
        /**计划开始日期不能为空*/
        Assert.notNull( sfc.getPlanStartTime(), I18nUtil.getI18nText( "production.sfc.planStartTimeNotNull" ) );
    }


    /**
     * 检查当天工令是否已开立
     *
     * @param sfc
     */
    public void checkSFCCreatedByDate( SFC sfc ){
        List<SFC> sfcList = sfcAdviceMapper.selectSFCListOnWorkByPlanStartDate( sfc.getSite(), sfc.getProcedureBo(), sfc.getItemBo(), sfc.getPlanStartTime() );
        if( sfcList != null && sfcList.size() > 0 ){
            throw BusinessException.build( "production.sfc.sfcIsCreated", null );
        }
    }

    /**
     * 查找工令最大允许开立日期
     *
     * @param sfc
     * @return
     */
    public Long findSFCMaxOverDays( SFC sfc ){

        String ruleValue = systemRuleAdviceService.existSystemRule( sfc.getSite(), Constants.SYSTEM_RULE_MAX_OVER_DAYS );
        return NumberUtils.createLong( ruleValue );
    }

    /**
     * 检查计划日期是否超过限制
     *
     * @param sfc
     */
    public void checkPlanStartDate( SFC sfc ){
        Date planStartDate = sfc.getPlanStartTime();
        Date now = DateUtils.truncate( new Date(), Calendar.DATE );
        if( planStartDate.before( now ) ){
            throw BusinessException.build( "production.sfc.planStartDateInvalid", null );
        }
        Long maxOverDays = findSFCMaxOverDays( sfc );
        Date maxDate = DateUtils.addDays( now, maxOverDays.intValue() );
        if( planStartDate.after( maxDate ) ){
            throw BusinessException.build( "production.sfc.planStartDateOver", null );
        }
    }

    /**
     * 创建SAP MES可追溯SFC
     *
     * @param sfc
     */
    public void createMesSFC( SFC sfc ){
        String sfcRef = null;
        try {
            SecurityContext.instance().setSite( sfc.getSite() );
            SFCBOInterface sfcServicesAdaptee = ServiceLocator.getService( "SFCBO" );
            Data inData = new Data();
            inData.put( "SITE", sfc.getSite() );
            inData.put( "ITEM_BO", new BOHandle( sfc.getItemBo() ) );
            inData.put( "QTY", BigDecimal.valueOf( sfc.getSfcQty() ) );
            inData.put( "USER_BO", new BOHandle( CommonMethods.getUserBo() )  );
            inData.put( "SHOP_ORDER_TYPE_BO", new BOHandle("ShopOrderTypeBO:" + sfc.getSite() + ",PRODUCTION" ) );
            Data releaseData = sfcServicesAdaptee.release( inData );
            sfcRef = ((Data)releaseData.get( "SFCS" )).getString( "SFC_BO" );
        } catch (BasicBOBeanException e) {
            throw BusinessException.build( e.toString() );
        }
        if( StringUtils.isNotBlank( sfcRef ) ){
            SFCBOHandle sfcboHandle = new SFCBOHandle( sfcRef );
            sfc.setSfc( sfcboHandle.getSFC() );
            sfc.setHandle( sfcRef );
        }else{
            throw BusinessException.build( "production.sfc.trackableSFCCreateFail", null );
        }
    }

    /**
     * 调整SAP MES 可追溯SFC数量
     *
     * @param sfc
     */
    public void adjustMesSfcQty( SFC sfc ){

        String sfcBo = BOHandleUtil.buildSFCHandle( sfc.getSite(), sfc.getSfc() );
        String operationBo = sfcStepAdviceService.findSfcFirstOperation( sfcBo );
        try {
            SfcStateServiceInterface sfcStateService = Services.getService( "com.sap.me.production", "SfcStateService", sfc.getSite() );
            AdjustSfcQuantityRequest adjustSfcQuantityRequest = new AdjustSfcQuantityRequest();
            adjustSfcQuantityRequest.setSfcRef( sfcBo );
            adjustSfcQuantityRequest.setNewSfcQuantity( BigDecimal.valueOf( sfc.getSfcQty() ) );
            adjustSfcQuantityRequest.setOperationRef( operationBo );
            List<AdjustSfcQuantityRequest> requestList = new ArrayList<AdjustSfcQuantityRequest>();
            requestList.add( adjustSfcQuantityRequest );
            sfcStateService.adjustSfcQuantity( requestList );
        } catch (com.sap.me.frame.domain.BusinessException e) {
            throw BusinessException.build( Exceptions.convert( e ).toString() );
        }
    }

    /**
     * 生成研磨工令编号
     *
     * @param sfc
     */
    public void generateSFCCode( SFC sfc ){
        String grindNumberRule = systemRuleAdviceService.existSystemRule( sfc.getSite(), Constants.SYSTEM_RULE_GRIND_NUMBER_RULE );

        try {
            NumberingServiceInterface numberingService = Services.getService( "com.sap.me.numbering", "NumberingService", sfc.getSite() );
            GenerateNextNumberRequest nextNumberRequest = new GenerateNextNumberRequest();
            nextNumberRequest.setNextNumberType( NextNumberTypeEnum.SFCSERIALIZATION );
            nextNumberRequest.setNumberOfValues( BigDecimal.ONE );
            nextNumberRequest.setItemRef( BOHandleUtil.buildItemHandleAllRev( sfc.getSite(), grindNumberRule ) );

            GenerateNextNumberResponse nextNumberResponse = numberingService.generateNextNumber( nextNumberRequest );
            sfc.setSfc( nextNumberResponse.getId().get( 0 ) );
            sfc.setHandle( BOHandleUtil.buildSFCHandle( sfc.getSite(), sfc.getSfc() ) );
        } catch (com.sap.me.frame.domain.BusinessException e) {
            throw BusinessException.build( Exceptions.convert( e ).toString() );
        }

    }

    /**
     * 删除工令
     *
     * @param sfc
     */
    @Transactional( propagation = Propagation.REQUIRED, rollbackFor = Exception.class )
    public void deleteSFC( SFC sfc ){
        /**站点不能为空*/
        siteAdviceService.existSite( sfc.getSite() );
        /**工令号不能为空*/
        Assert.hasText( sfc.getSfc(), I18nUtil.getI18nText("common.sfcNotBlank") );

        String handle = BOHandleUtil.buildSFCHandle( sfc.getSite(), sfc.getSfc() );
        SFC originalSFC = sfcService.selectSFCById( handle );

        /**工令号对应的工令是否存在*/
        Assert.notNull( originalSFC, I18nUtil.getI18nText( "production.sfc.sfcNotExist", new String[]{ sfc.getSfc() } ) );

        if( originalSFC.getReleasedQty() > 0 ){
            throw BusinessException.build( "production.sfc.sfcInUse", new String[]{ sfc.getSfc() } );
        }

        String procedure = fieldGroupAdviceService.getFieldName( originalSFC.getProcedureBo() );
        if( ProcedureEnum.CNC == ProcedureEnum.valueOf( procedure ) ){
            deleteMesSFC( originalSFC );
        }

        originalSFC.setStatusBo( "StatusBO:"+ sfc.getSite() +",409" );
        originalSFC.setUpdateUserBo( CommonMethods.getUserBo() );
        originalSFC.setActEndTime( new Date() );
        sfcService.updateSFC( originalSFC );
    }

    /**
     * 删除mes sfc
     *
     * @param sfc
     */
    public void deleteMesSFC( SFC sfc ){
        try{
            ScrapDeleteServiceInterface scrapDeleteService = Services.getService( "com.sap.me.production", "ScrapDeleteService", sfc.getSite() );
            DeleteSfcRequest deleteSfcRequest = new DeleteSfcRequest();
            deleteSfcRequest.setSfcRef( sfc.getHandle() );
            deleteSfcRequest.setQuantity( BigDecimal.valueOf( sfc.getSfcQty() ) );
            scrapDeleteService.deleteSfc( deleteSfcRequest );
        } catch (com.sap.me.frame.domain.BusinessException e) {
            throw BusinessException.build( Exceptions.convert( e ).toString() );
        }
    }
}
